#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>

#include <string>
#include <list>
#include <vector>
#include <set>

#ifdef WIN32
  #include <windows.h>
#else
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <unistd.h>
  #include <fcntl.h>
#endif

#include "cpp.h"

typedef std::vector<TokenDesc> TokenVector;

static bool g_header_file_p = false;

static bool g_inside_class_p = false;

static std::set<std::string> g_cpp_reserved_words;

static std::string g_filename;

// the policy of this parser is "loose"
// and the [mod] below is "modification"
// like "extern" "static" "inline" "template" ...
// because the modification doesn't affect our law,
// we don't need that, ignored in parsing.

// exception
class Exception {
public:
    std::string         desc;

    int                 line;

    int                 source_line;

    Exception(const char *d, int l, int sl)
        : desc(d), line(l), source_line(sl)
    {
    }
};

#define E_UNKNOW_TOKEN          Exception("unknow token", lineno, __LINE__)

static void top_level();
static void proc_typedef();
static void proc_macro_define(const char *text, int lineno);
static void ignore_until_token(int token);
static void proc_struct_and_class(int tk);
static bool try_function();
static void ignore_using();
static bool try_variable_define();
static void ignore_pair(int left, int right, bool);
static void print_look_ahead_token();
static void proc_function_define();
static void proc_enum();

// reserve the tag's context.
// ex: class -> method -> local variable.
// the class in depth 1, and method in depth 2
// and so on.
// when query a tag in a editor, we found the
// context in the source, and send
// the query like: tag parent-tag, we
// will reduce the result set...
struct Tag;

typedef std::vector<Tag *> TagVector;

struct Tag {
    int                 lineno;

    std::string         text;

    const char         *type;

    TagVector           children;

    Tag                *parent;

    int                 depth;

    Tag(Tag *p) {
        parent = p;
        if (p)
            depth = p->depth + 1;
    }

    ~Tag() {
        int i;
        for (i = 0; i < (int) children.size(); i++) {
            delete children[i];
        }
    }
};

static Tag             *g_global_tag = NULL;

static Tag             *g_tag_context = NULL;

static void init_tag_tree()
{
    if (g_global_tag) {
        delete g_global_tag;
        g_global_tag = NULL;
    }

    g_global_tag = new Tag(NULL);

    g_global_tag->depth  = -1;

    g_tag_context = g_global_tag;
}

static void result_put(const char *text,
                       int         lineno,
                       const char *type)
{
    Tag *t = new Tag(g_tag_context);
    t->lineno = lineno;
    t->text   = text;
    t->type   = type;
    g_tag_context->children.push_back(t);
}

// all types list here

#define RESULT_PUT_VARIABLE(lineno, text)       \
    result_put(lineno, text, "variable")

#define RESULT_PUT_CLASS(lineno, text)          \
    result_put(lineno, text, "class")

#define RESULT_PUT_STRUCT(lineno, text)         \
    result_put(lineno, text, "struct")

#define RESULT_PUT_ENUM(lineno, text)           \
    result_put(lineno, text, "enum")

#define RESULT_PUT_UNION(lineno, text)          \
    result_put(lineno, text, "union")

#define RESULT_PUT_FUNCTION(lineno, text)       \
    result_put(lineno, text, "function")

#define RESULT_PUT_REFERENCE(lineno, text)      \
    result_put(lineno, text, "reference")

#define RESULT_PUT_MACRO(lineno, text)          \
    result_put(lineno, text, "macro")

#define RESULT_PUT_TYPEDEF(lineno, text)        \
    result_put(lineno, text, "typedef")

#define RESULT_PUT_ENUM_ITEM(lineno, text)      \
    result_put(lineno, text, "enum_item")

#define RESULT_PUT_DUMMY()                      \
    result_put("", 0, "dummy")

static void tag_tree_follow()
{
    if (g_tag_context->children.empty())
        abort();

    g_tag_context = g_tag_context->children.back();
}

static void tag_tree_up()
{
    if (g_tag_context->parent == NULL)
        abort();

    g_tag_context = g_tag_context->parent;
}

static void tag_tree_dump_ident(Tag *tag)
{
    int i;

    if (tag->depth > 0) {
        for (i = 0; i < tag->depth; i++) {
            printf("    ");
        }
    }

    if (tag->depth >= 0) {
        printf("%d\t%s\t%s\n", tag->lineno, tag->text.c_str(), tag->type);
    }

    for (i = 0; i < (int) tag->children.size(); i++) {
        tag_tree_dump_ident(tag->children[i]);
    }
}

static void print_quote_string(const char *str)
{
    std::string res;
    char        c;
    for (;;) {
        c = *str++;
        if (!c)
            break;
        if (c == '\\' || c == ' ' || c == '(' || c == ')')
            res.push_back('\\');
        res.push_back(c);
    }

    if (res.size() > 0) {
        fwrite(res.c_str(), 1, res.size(), stdout);
    }
}

static void tag_tree_dump_quote(Tag *tag)
{
    int i;

    printf("(");

    if (tag->depth >= 0) {
        printf("%d ", tag->lineno);
        print_quote_string(tag->text.c_str());
        printf(" ");
        print_quote_string(tag->type);
    }

    for (i = 0; i < (int) tag->children.size(); i++) {
        tag_tree_dump_quote(tag->children[i]);
    }

    printf(")");
}

static void init_cpp_reserved_words()
{
    if (g_cpp_reserved_words.empty()) {
        // init the cpp reserved words map
        g_cpp_reserved_words.insert("try");
        g_cpp_reserved_words.insert("catch");
        g_cpp_reserved_words.insert("finally");
        g_cpp_reserved_words.insert("if");
        g_cpp_reserved_words.insert("else");
        g_cpp_reserved_words.insert("do");
        g_cpp_reserved_words.insert("while");
        g_cpp_reserved_words.insert("for");
        g_cpp_reserved_words.insert("switch");
        g_cpp_reserved_words.insert("case");
        g_cpp_reserved_words.insert("default");
        g_cpp_reserved_words.insert("enum");
        g_cpp_reserved_words.insert("union");
        g_cpp_reserved_words.insert("struct");
        g_cpp_reserved_words.insert("class");
        g_cpp_reserved_words.insert("template");
        g_cpp_reserved_words.insert("typename");
        g_cpp_reserved_words.insert("typedef");
        g_cpp_reserved_words.insert("namespace");
        g_cpp_reserved_words.insert("sizeof");
        g_cpp_reserved_words.insert("new");
        g_cpp_reserved_words.insert("delete");
        g_cpp_reserved_words.insert("continue");
        g_cpp_reserved_words.insert("break");
        g_cpp_reserved_words.insert("asm");
        g_cpp_reserved_words.insert("__asm__");
        g_cpp_reserved_words.insert("attribute");
        g_cpp_reserved_words.insert("__attribute__");
        g_cpp_reserved_words.insert("mutable");
        g_cpp_reserved_words.insert("const_cast");
        g_cpp_reserved_words.insert("static_cast");
        g_cpp_reserved_words.insert("dynamic_cast");
        g_cpp_reserved_words.insert("reinterpret_cast");
        g_cpp_reserved_words.insert("inline");
        g_cpp_reserved_words.insert("return");
        g_cpp_reserved_words.insert("return");
        g_cpp_reserved_words.insert("goto");
        g_cpp_reserved_words.insert("throw");
        g_cpp_reserved_words.insert("volatile");
        g_cpp_reserved_words.insert("__volatile");
        g_cpp_reserved_words.insert("static");
        g_cpp_reserved_words.insert("extern");
        g_cpp_reserved_words.insert("using");
        g_cpp_reserved_words.insert("public");
        g_cpp_reserved_words.insert("protected");
        g_cpp_reserved_words.insert("private");
        g_cpp_reserved_words.insert("register");
        g_cpp_reserved_words.insert("this");
        g_cpp_reserved_words.insert("friend");
        g_cpp_reserved_words.insert("explicit");
        g_cpp_reserved_words.insert("typeid");
        g_cpp_reserved_words.insert("operator");
        g_cpp_reserved_words.insert("const");
    }
}

static bool is_header_file_name(const char *fn)
{
    int len = strlen(fn);

    if (len > 0) {
        int i;
        for (i = len - 1; i >= 0; i--) {
            if (fn[i] == '.')
                break;
        }
        if (i >= 0) {
            ++i;
            return (fn[i] == 'h' || fn[i] == 'H');
        }
    }

    return true;
}

static void proc_struct_and_class_wrap(int tk)
{
    g_inside_class_p = true;
    proc_struct_and_class(tk);
    g_inside_class_p = false;
}

// in the main scope, accept
// * global variable declare
//   [mod] variable-name ;
// * function declare
//   [mod] function-name ( arguments ) [mod] ;
// * function define
//   [mod] function-name ( argument ) [mod] { [...] }
// * typedef statement
//   typedef old-type new-type ;
//   old-type can be struct class enum ...
// * struct and class and enum and union declare
//   [mod] {struct|class|enum|union} name [mod] { [...] }

static void top_level()
{
    std::string text;
    int lineno;

    for (;;) {
        int t = lex_get_token(text, &lineno);

        switch (t) {
        case tkIDENT:
            lex_unput_token(t, text.c_str(), lineno);
            if (!try_function()) {
                if (!try_variable_define()) {
                    lex_get_token(text, &lineno);
                }
            }
            break;

        case rwCLASS:
        case rwSTRUCT:
        case rwUNION:
            proc_struct_and_class_wrap(t);
            break;

        case rwENUM:
            proc_enum();
            break;

        case tkMACRO_DEFINE:
            proc_macro_define(text.c_str(), lineno);
            break;

        case rwTYPEDEF:
            proc_typedef();
            break;

        case rwTEMPLATE:
            ignore_pair(tkLEFTA, tkRIGHTA, false);
            break;

            // try inline function.
        case rwINLINE:
            break;

        case rwATTRIBUTE:
        case rwASM:
            ignore_pair(tkLEFTP, tkRIGHTP, false);
            break;

        case rwUSING:
            ignore_using();
            break;

        case 0:
            return;
            break;

        case -1:
            throw E_UNKNOW_TOKEN;
            break;
        }
    }
}

// typedef old-type new-type ;
// typedef std::vector<int> IntVector;
// typedef struct _Persion { std::string name; int age; } Persion;
// in case of typedef struct, ignore the _Persion,
// only the last new-type "Persion".
static void proc_typedef()
{
    TokenVector tokens;
    std::string text;
    int         lineno;
    int         r;
    int         depth;

    r = lex_get_token(text, &lineno);

    bool found_leftb;

    if (r == rwSTRUCT || r == rwCLASS || r == rwUNION) {
        depth = 0;
        int tk_bak = r;
        for (;;) {
            r = lex_get_token(text, &lineno);

            if (r > 0)
                tokens.push_back(TokenDesc(r, text.c_str(), lineno));

            if (r == tkLEFTB) {
                found_leftb = true;
                ++depth;
            }
            else if (r == tkRIGHTB)
                --depth;

            else if (r == tkTERM && depth <= 0) {
                TokenDesc bak = tokens.back();
                tokens.pop_back();

                if (tokens.size() > 0) {
                    TokenDesc& tk = tokens.back();

                    if (tk.token == tkIDENT) {
                        RESULT_PUT_TYPEDEF(tk.text.c_str(), tk.lineno);

                        tokens.pop_back();
                    }
                }

                tokens.push_back(bak);

                int i;
                int ntokens = tokens.size();
                for (i = ntokens - 1; i >= 0; i--) {
                    TokenDesc& tk = tokens[i];
                    lex_unput_token(tk.token, tk.text.c_str(), tk.lineno);
                }

                proc_struct_and_class_wrap(tk_bak);
                return;
            }

            else if (r == -1)
                throw E_UNKNOW_TOKEN;

            else if (r == 0)
                return;
        }
    }
    else if (r == rwENUM) {
        depth = 0;
        for (;;) {
            r = lex_get_token(text, &lineno);

            if (r > 0)
                tokens.push_back(TokenDesc(r, text.c_str(), lineno));
            else if (r == -1)
                throw E_UNKNOW_TOKEN;

            if (r == tkTERM || r == 0) {
                // check the last token
                tokens.pop_back();

                if (tokens.size() > 0) {
                    TokenDesc& tk = tokens.back();

                    if (tk.token == tkIDENT) {
                        RESULT_PUT_TYPEDEF(tk.text.c_str(), tk.lineno);

                        tokens.pop_back();
                    }
                }

                int i;
                int ntokens = tokens.size();
                for (i = ntokens - 1; i >= 0; i--) {
                    TokenDesc& tk = tokens[i];
                    lex_unput_token(tk.token, tk.text.c_str(), tk.lineno);
                }

                proc_enum();

                return;
            }
        }
    }
    else {
        tokens.clear();

        lex_unput_token(r, text.c_str(), lineno);

        for (;;) {
            r = lex_get_token(text, &lineno);
            if (r == tkTERM || r == 0) {
                if (tokens.size() > 0) {
                    TokenDesc& tk = tokens.back();
                    if (tk.token == tkIDENT) {
                        RESULT_PUT_TYPEDEF(tk.text.c_str(), tk.lineno);
                    }
                    else if (tk.token == tkRIGHTP) {
                        int i;
                        for (i = 0; i < (int) tokens.size(); i++) {
                            if (tokens[i].token == tkLEFTP) {
                                if (i + 2 < (int) tokens.size()
                                    && tokens[i + 1].token == tkPTR
                                    && tokens[i + 2].token == tkIDENT) {
                                    RESULT_PUT_TYPEDEF(tokens[i + 2].text.c_str(),
                                                       tokens[i + 2].lineno);
                                    break;
                                }
                            }
                        }
                    }
                }

                break;
            }

            else if (r == -1)
                throw E_UNKNOW_TOKEN;

            tokens.push_back(TokenDesc(r, text.c_str(), lineno));
        }
    }
}

// the hand writed lexer for macro
// maybe it's better to handle macro like
// normal source file, just remove the `\'
// at the end of line

static bool _reserved_words_setup = false;

static void parse_func_ref_in_macro_define(const char *text,
                                           int         lineno)
{
    std::string name;
    const char *p;
    const char *end;
    int last_token;
    int last_lineno;

    p = text;
    end = strlen(p) + p;

    if (_reserved_words_setup == false) {
        _reserved_words_setup = true;
        init_cpp_reserved_words();
    }

    char c;
    last_token = 0;
    while (p < end) {
        // ident
        c = *p++;

        if (c == '_'
            || (c >= 'a' && c <= 'z')
            || (c >= 'A' && c <= 'Z')) {
            const char *ident_beg = p - 1;
            while (p < end) {
                c = *p;
                if (c == '_'
                    || (c >= 'a' && c <= 'z')
                    || (c >= 'A' && c <= 'Z')
                    || (c >= '0' && c <= '9')) {
                    ++p;
                }
                else
                    break;
            }

            name.assign(ident_beg, p - ident_beg);

            if (g_cpp_reserved_words.find(name) == g_cpp_reserved_words.end()) {
                last_token = tkIDENT;
                last_lineno = lineno;
            }
            else
                last_token = 0;
        }
        else if (c == '\r') {
            if (p < end && *p == '\n')
                ++p;
            ++lineno;
        }
        else if (c == '\n')
            ++lineno;
        else if (c == '/' && (p < end && *p == '/')) {
            while (p < end) {
                c = *p++;
                if (c == '\r') {
                    if (p < end && *p == '\n')
                        ++p;
                    ++lineno;
                    break;
                }
                else if (c == '\n') {
                    ++lineno;
                    break;
                }
            }
        }
        else if (c == '/' && (p < end && *p == '*')) {
            char last = 0;
            ++p;
            while (p < end) {
                c = *p++;

                if (last == '*' && c == '/')
                    break;

                last = c;
            }
        }
        else if (c == '"') {
            char last = 0;
            while (p < end) {
                c = *p++;

                if (last != '\\' && c == '"')
                    break;
            }
            last_token = 0;
        }
        // put the function ref
        else if (c == '(' && last_token == tkIDENT) {
            RESULT_PUT_REFERENCE(name.c_str(), last_lineno);
        }
        else if (c != ' ' && c != '\t')
            last_token = c;
    }
}

// #define xxxx yyyy
static void proc_macro_define(const char *text, int lineno)
{
    const char *tmp = (const char *) strstr(text, "define");

    if (tmp == NULL) {
        fprintf(stderr, "invalid #define: `%s': %d\n", text, lineno);
        abort();
    }

    assert(tmp != NULL && "invalid #define");

    text = tmp + 6;

    char c;
    for (; ; ) {
        c = *text;
        if (c == ' ' || c == '\t')
            ++text;
        else
            break;
    }

    tmp = text;

    for (; ; ) {
        c = *tmp;
        if (c == '_' || (c >= 'a' && c <= 'z')
            || (c >= 'A' && c <= 'Z')
            || (c >= '0' && c <= '9'))
            ++tmp;
        else
            break;
    }

    std::string macro(text, tmp - text);

    const char *ident_end = tmp;

    // eval the line number where ident in
    for (;;) {
        text = (const char *) strchr (tmp, '\n');
        if (text == NULL)
            text = (const char *) strchr (tmp, '\r');
        if (text) {
            tmp = text + 1;
            --lineno;
        }
        else
            break;
    }

    RESULT_PUT_MACRO(macro.c_str(), lineno);
    tag_tree_follow();
    parse_func_ref_in_macro_define(ident_end, lineno);
    tag_tree_up();
}

static void proc_enum()
{
    std::string text, last_text;
    int         lineno, last_lineno;
    int         r, last_token;

    last_token = 0;

    bool follow = false;

    // make sure it's like
    // enum name {};
    // or enum {};
    {
        TokenVector tokens;
        bool        yes = true;
        for (;;) {
            r = lex_get_token(text, &lineno);

            if (r > 0)
                tokens.push_back(TokenDesc(r, text.c_str(), lineno));
            else {
                yes = false;
                break;
            }

            if (r != tkIDENT) {
                if (r != tkLEFTB)
                    yes = false;
                break;
            }
        }
        // unput all tokens
        int i;
        int ntokens = tokens.size();
        for (i = ntokens - 1; i >= 0; i--) {
            TokenDesc& tk = tokens[i];
            lex_unput_token(tk.token, tk.text.c_str(), tk.lineno);
        }
        if (!yes)
            return;
    }

    for (;;) {
        r = lex_get_token(text, &lineno);

        if (r == tkIDENT) {
            last_token  = r;
            last_lineno = lineno;
            last_text   = text;
        }
        else if (r == tkLEFTB || r == 0){
            if (last_token == tkIDENT) {
                RESULT_PUT_ENUM(last_text.c_str(), last_lineno);
                tag_tree_follow();
                follow = true;
            }
            break;
        }
        else if (r == -1)
            throw E_UNKNOW_TOKEN;
    }

    // the enum define
    // enum name { item1 [ = value], ... } ;

    int depth = 1;

    last_token = 0;
    for (;;) {
        r = lex_get_token(text, &lineno);

        if (r == tkIDENT) {
            last_token  = tkIDENT;
            last_lineno = lineno;
            last_text   = text;
        }
        else if (r == tkASS) {
            if (last_token == tkIDENT) {
                RESULT_PUT_ENUM_ITEM(last_text.c_str(), last_lineno);
            }

            // ignore until `,'
            last_token = 0;
            for (;;) {
                r = lex_get_token(text, &lineno);

                if (r == tkCOMMA || r == 0 || r == tkTERM)
                    break;
                else if (r == tkRIGHTB) {
                    lex_unput_token(r, text.c_str(), lineno);
                    break;
                }
                else if (r == tkLEFTB)
                    ++depth;
                else if (r == -1)
                    throw E_UNKNOW_TOKEN;
            }
        }
        else if (r == tkCOMMA) {
            if (last_token == tkIDENT) {
                RESULT_PUT_ENUM_ITEM(last_text.c_str(), last_lineno);
            }

            last_token = 0;
        }
        else if (r == 0) {
            break;
        }
        else if (r == tkLEFTB)
            ++depth;
        else if (r == tkRIGHTB) {
            if (!--depth) {
                if (last_token == tkIDENT) {
                    RESULT_PUT_ENUM_ITEM(last_text.c_str(), last_lineno);
                }
                break;
            }
        }
        else if (r == -1)
            throw E_UNKNOW_TOKEN;
    }

    if (follow)
        tag_tree_up();
}

static void ignore_until_token(int token)
{
    std::string text;
    int lineno;

    for (;;) {
        int r = lex_get_token(text, &lineno);
        if (r == token) {
            lex_unput_token(r, text.c_str(), lineno);
            break;
        }
        else if (r == 0)
            break;
        else if (r == -1)
            throw E_UNKNOW_TOKEN;
    }
}

static void fetch_variable_from_tokens(TokenVector& tokens,
                                       int          begin,
                                       bool         skip_type)
{
    int i, ntokens = tokens.size();

    i = begin;

    // main type
    // Parent::Child style
    if (skip_type) {
        bool ex = true;
        for (; i < ntokens; i++) {
            TokenDesc& tk = tokens[i];

            if (ex) {
                if (tk.token != tkIDENT)
                    break;
            }
            else {
                if (tk.token != tkCC)
                    break;
            }

            ex = !ex;
        }

        // remove the <> template
        if (i < ntokens && tokens[i].token == tkLEFTA) {
            int depth = 1;
            for (i++; i < ntokens && depth > 0; i++) {
                if (tokens[i].token == tkRIGHTA)
                    --depth;
                else if (tokens[i].token == tkLEFTA)
                    ++depth;
            }
        }
    }

    if (i >= ntokens)
        return;

    for (;;) {
        // perhas * & before the var-name
        // ignore them
        while (i < ntokens) {
            int token = tokens[i].token;
            if (token == tkREF
                || token == tkPTR
                || token == rwCONST
                || token == rwATTRIBUTE
                || token == rwASM) {
                ++i;
                if (i < ntokens && (token == rwATTRIBUTE || token == rwASM)
                    && tokens[i].token == tkLEFTP) {
                    ++i;
                    int depth = 1;
                    for (; i < ntokens; i++) {
                        if (tokens[i].token == tkLEFTP) {
                            ++depth;
                        }
                        else if (tokens[i].token == tkRIGHTP) {
                            --depth;
                            if (depth == 0) {
                                ++i;
                                break;
                            }
                        }
                    }
                }
            }
            else
                break;
        }

        if (i >= ntokens)
            break;

        // it must be an ident
        if (tokens[i].token != tkIDENT) {

            // perhaps it's the defined like
            // struct S {
            //   unsigned v : 4;
            //   unsigned x : 4;
            // };
            // don't warning at this case.

            if (tokens[i].token != tkCOLON)
                fprintf(stderr, "`%s' is not ident %d, it's not the variable, file `%s'\n",
                        tokens[i].text.c_str(),
                        tokens[i].lineno, g_filename.c_str());
            break;
        }

        // look ahead, if it's a ident too,
        // maybe this is a modification
        std::string last_text;
        int last_lineno;

        last_text = tokens[i].text;
        last_lineno = tokens[i].lineno;

        while (i + 1 < ntokens) {
            int token = tokens[i + 1].token;
            if (token == tkIDENT
                || token == tkCC
                || token == tkPTR
                || token == tkREF
                || token == rwCLASS
                || token == rwSTRUCT
                || token == rwENUM
                || token == rwUNION
                || token == rwCONST
                || token == rwATTRIBUTE) {
                if (token == tkIDENT) {
                    last_text = tokens[i + 1].text;
                    last_lineno = tokens[i + 1].lineno;
                }
                ++i;
                if (i < ntokens && (token == rwATTRIBUTE || token == rwASM)
                    && tokens[i].token == tkLEFTP) {
                    ++i;
                    int depth = 1;
                    for (; i < ntokens; i++) {
                        if (tokens[i].token == tkLEFTP)
                            ++depth;
                        else if (tokens[i].token == tkRIGHTP) {
                            --depth;
                            if (depth == 0) {
                                ++i;
                                break;
                            }
                        }
                    }
                }
            }
            else
                break;
        }

        RESULT_PUT_VARIABLE(last_text.c_str(),
                            last_lineno);

        if (++i >= ntokens)
            break;

        // array declare: type name[size]
        // object init: type name(arguments)
        while (i < ntokens
               && (tokens[i].token == tkLEFTS
                   || tokens[i].token == tkLEFTP)) {
            int left, right, depth;
            if (tokens[i].token == tkLEFTS) {
                left = tkLEFTS;
                right = tkRIGHTS;
            }
            else {
                left = tkLEFTP;
                right = tkRIGHTP;
            }
            depth = 1;
            for (++i; i < ntokens; ++i) {
                if (tokens[i].token == right) {
                    if (!--depth) {
                        ++i;
                        break;
                    }
                }
                else if (tokens[i].token == left)
                    ++depth;
            }
        }

        if (i >= ntokens)
            break;


        if (tokens[i].token == tkLEFTS) {
            for (;;) {
                ++i;
                if (i >= ntokens)
                    break;

                int depth = 1;
                for (;;) {
                    TokenDesc& tk = tokens[i++];

                    if (tk.token == tkLEFTS)
                        ++depth;
                    else if (tk.token ==tkRIGHTS) {
                        --depth;
                        if (depth == 0)
                            break;
                    }

                    if (i >= ntokens)
                        break;
                }

                if (i >= ntokens)
                    break;

                if (tokens[i].token != tkLEFTS)
                    break;
            }
        }

        if (i >= ntokens)
            break;

        // attribute
        if (tokens[i].token == rwATTRIBUTE || tokens[i].token == rwASM) {
            ++i;
            if (i >= ntokens)
                break;

            if (tokens[i].token == tkLEFTP) {
                int depth = 0;
                for (; i < ntokens; i++) {
                    if (tokens[i].token == tkLEFTP)
                        ++depth;
                    else if (tokens[i].token == tkRIGHTP) {
                        if (!--depth) {
                            ++i;
                            break;
                        }
                    }
                }
            }
        }

        if (i >= ntokens)
            break;

        // if there is a initialize expr like
        // type name = init-val
        // ignore the right hand size value
        if (tokens[i].token == tkASS) {
            ++i;
            if (i >= ntokens)
                break;

            /* Token token = { 1, "text", 2 };
             */
            bool done = false;
            while (!done && i < ntokens) {
                int depth;
                int left, right;

                if (tokens[i].token == tkLEFTB
                    || tokens[i].token == tkLEFTP) {
                    depth = 1;

                    if (tokens[i].token == tkLEFTB) {
                        left = tkLEFTB;
                        right = tkRIGHTB;
                    }
                    else {
                        left = tkLEFTP;
                        right = tkRIGHTP;
                    }

                    if (tokens[i].token == tkLEFTP) {
                        if (i > 0 && tokens[i - 1].token == tkIDENT) {
                            RESULT_PUT_REFERENCE(tokens[i - 1].text.c_str(),
                                                tokens[i - 1].lineno);
                        }
                    }

                    ++i;

                    for (; i < ntokens; i++) {
                        if (tokens[i].token == right) {
                            --depth;
                            if (depth == 0)
                                break;
                        }
                        else if (tokens[i].token == left)
                            ++depth;
                        if (tokens[i].token == tkLEFTP) {
                            if (i > 0 && tokens[i - 1].token == tkIDENT) {
                                RESULT_PUT_REFERENCE(tokens[i - 1].text.c_str(),
                                                    tokens[i - 1].lineno);
                            }
                        }
                    }
                }
                else {
                    if (tokens[i].token == tkCOMMA
                        || tokens[i].token == tkTERM) {
                        done = true;
                    }

                    ++i;
                }
            }

            if (i >= ntokens)
                break;
        }

        if (tokens[i].token == tkCOMMA) {
            ++i;
            if (i >= ntokens)
                break;
        }
    }
}

// class == struct
// {class|struct} name [: inherit] { [...] } ;
// or just declare like {class|struct} name ;
// or define variable like struct Token token;
static void proc_struct_and_class(int the_type)
{
    std::string text;
    int         lineno;
    int         r;
    TokenVector tokens;
    bool        class_define_p;
    bool        done;

    class_define_p = true;
    done           = false;
    while (!done) {
        r = lex_get_token(text, &lineno);

        if (r > 0)
            tokens.push_back(TokenDesc(r, text.c_str(), lineno));

        switch (r) {
            // begin class define
        case tkLEFTB:
            if (class_define_p)
                done = true;
            break;

            // class inherit
            // ignore until {
        case tkCOLON:
            if (class_define_p) {
                ignore_until_token(tkLEFTB);
                r = lex_get_token(text, &lineno);
                if (r == tkLEFTB) {
                    done = true;
                }
                else if (r == 0)
                    return;
                else if (r == -1)
                    throw E_UNKNOW_TOKEN;
            }
            break;

            // the case
            // template <>
            // struct name<type> {
            //     ...
            // };
        case tkLEFTA:
        {
            int depth = 1;
            for (;;) {
                r = lex_get_token(text, &lineno);
                if (r == tkLEFTA)
                    ++depth;
                else if (r == tkRIGHTA) {
                    if (!--depth)
                        break;
                }
                else if (r == 0)
                    break;
                else if (r == -1)
                    throw E_UNKNOW_TOKEN;
            }
        }
            break;

            // ;
        case tkTERM:
            class_define_p = false;
            done           = true;
            break;

        case 0:
            return;
            break;

        case -1:
            throw E_UNKNOW_TOKEN;
            break;

        default:
            if (r != tkIDENT && r != tkCC) {
                class_define_p = false;
                done           = true;
            }
            break;
        }
    }

    // ;
    if (!class_define_p) {
        int i;
        int ntokens = tokens.size();
        for (i = ntokens - 1; i >= 0; i--) {
            lex_unput_token(tokens[i].token,
                            tokens[i].text.c_str(),
                            tokens[i].lineno);
        }
        return;
    }

    bool follow = false;

    // pop the {

    tokens.pop_back();

    if (tokens.size() > 0) {
        TokenDesc& last = tokens.back();

        if (last.token == tkIDENT) {
            if (the_type == rwCLASS)
                RESULT_PUT_CLASS(last.text.c_str(), last.lineno);
            else if (the_type == rwSTRUCT)
                RESULT_PUT_STRUCT(last.text.c_str(), last.lineno);
            else
                RESULT_PUT_UNION(last.text.c_str(), last.lineno);

            follow = true;
            tag_tree_follow();
        }
    }

    // we found {
    // parsing the class define.
    int depth = 1;
    while (depth > 0) {
        r = lex_get_token(text, &lineno);

        switch (r) {
            // ignore the modification
        case rwPUBLIC:
        case rwPROTECTED:
        case rwPRIVATE:
            ignore_until_token(tkCOLON);
            break;

        case rwENUM:
            proc_enum();
            break;

        case tkMACRO_DEFINE:
            proc_macro_define(text.c_str(), lineno);
            break;

        case rwTYPEDEF:
            proc_typedef();
            break;

        case rwCLASS:
        case rwSTRUCT:
        case rwUNION:
            proc_struct_and_class_wrap(r);
            break;

        case tkIDENT:
            lex_unput_token(r, text.c_str(), lineno);
            if (!try_function()) {
                if (!try_variable_define())
                    lex_get_token(text, &lineno);
            }
            break;

        case rwUSING:
            ignore_using();
            break;

        case rwTEMPLATE:
            ignore_pair(tkLEFTA, tkRIGHTA, false);
            break;

        case tkLEFTB:
            ++depth;
            break;

        case tkRIGHTB:
            --depth;
            break;

        case 0:
            depth = 0;
            break;

        case -1:
            throw E_UNKNOW_TOKEN;
            break;
        }
    }

    if (follow)
        tag_tree_up();

    // at the last of a class define
    // may be a variable define
    tokens.clear();

    done = false;
    while (!done) {
        r = lex_get_token(text, &lineno);

        switch (r) {
        case tkTERM:
            done = true;
            break;

        case 0:
            done = true;
            break;

        case -1:
            throw E_UNKNOW_TOKEN;
            break;

        default:
            tokens.push_back(TokenDesc(r, text.c_str(), lineno));
            break;
        }
    }

    if (tokens.size() > 0) {
        fetch_variable_from_tokens(tokens, 0, false);
    }
}

static void ignore_using()
{
    ignore_until_token(tkTERM);
}

static void ignore_pair(int left, int right, bool skip_first)
{
    std::string text;
    int lineno;
    int r;

    if (!skip_first) {
        r = lex_get_token(text, &lineno);
        if (r != left) {
            if (r == -1)
                throw E_UNKNOW_TOKEN;
            else if (r > 0)
                lex_unput_token(r, text.c_str(), lineno);

            return;
        }
    }

    int depth = 1;
    for (;;) {
        r = lex_get_token(text, &lineno);

        if (r == left)
            ++depth;
        else if (r == right) {
            if (!--depth)
                return;
        }
        else if (r == 0)
            return;
        else if (r == -1)
            throw E_UNKNOW_TOKEN;
    }
}

static void print_look_ahead_token()
{
    std::string text;
    int lineno;
    int r;
    r = lex_get_token(text, &lineno);
    if (r > 0) {
        printf("`%s' %d\n", text.c_str(), lineno);
        lex_unput_token(r, text.c_str(), lineno);
    }
    else if (r == 0)
        printf("eof\n");
    else if (r == -1)
        throw E_UNKNOW_TOKEN;
}

// [mod] return-type function-name ( argument ) ...
// if follow `;' it's the function declare.
// if follow `{' it's the function define.
static bool try_function()
{
    TokenVector tokens;

    std::string text;
    int         lineno;
    int         last_token;
    int         r;
    bool        found_leftp;
    bool        done;
    bool        operator_function_p;

    found_leftp         = false;
    done                = false;
    operator_function_p = false;
    while (!done) {
        r = lex_get_token(text, &lineno);

        switch (r) {
        case tkLEFTP:
            if (last_token != rwOPERATOR) {
                // look ahead token
                // if it's the *, maybe it's the function pointer
                int lineno_bak = lineno;
                std::string text_bak = text;
                r = lex_get_token(text, &lineno);
                if (r == tkPTR) {
                    done = true;
                    lex_unput_token(r, text.c_str(), lineno);
                    lex_unput_token(tkLEFTP, text_bak.c_str(), lineno_bak);
                }
                else {
                    lex_unput_token(r, text.c_str(), lineno);
                    found_leftp = true;
                    done = true;
                }
            }
            else
                tokens.push_back(TokenDesc(r, text.c_str(), lineno));
            break;

        case 0:
            done = true;
            break;

        case -1:
            throw E_UNKNOW_TOKEN;
            break;

            // <> template
        case tkLEFTA:
        {
            if (operator_function_p == false) {
                int  anchor    = tokens.size();
                int  depth     = 1;
                bool predicate = true;
                tokens.push_back(TokenDesc(r, text.c_str(), lineno));

                while (predicate) {
                    r = lex_get_token(text, &lineno);

                    if (r > 0)
                        tokens.push_back(TokenDesc(r, text.c_str(), lineno));

                    if (r == tkRIGHTA) {
                        if (!--depth)
                            break;
                    }
                    else if (r == tkLEFTA)
                        ++depth;
                    else if (r == 0)
                        break;
                    else if (r == -1)
                        throw E_UNKNOW_TOKEN;
                    else if (r == tkTERM || r == tkLEFTP || r == tkLEFTB) {
                        predicate = false;
                    }
                }

                // if yes, we pop the template part
                if (predicate)
                    tokens.resize(anchor);
                else
                    done = true;
            }
        }
            break;

        case rwVOLATILE:
        case rwINLINE:
        case rwSTATIC:
        case rwEXTERN:
        case rwREGISTER:
        case rwFRIEND:
        case rwEXPLICIT:
        case rwCONST:
            break;

        case rwOPERATOR:
            operator_function_p = true;
            break;

        case rwTEMPLATE:
            ignore_pair(tkLEFTA, tkRIGHTA, false);
            break;

        case rwATTRIBUTE:
            ignore_pair(tkLEFTP, tkRIGHTP, false);
            break;

        default:
            if (r > 0)
                tokens.push_back(TokenDesc(r, text.c_str(), lineno));

            if (!operator_function_p)
                if (r != tkIDENT && r != tkCC
                    && r != rwCONST && r != tkPTR
                    && r != tkREF) {
                    done = true;
                }

            // maybe it's the variable declare...
            if (r == tkTERM)
                done = true;
            break;
        }

        last_token = r;
    }

    if (found_leftp == false) {
        int i;
        int ntokens = tokens.size();

        for (i = ntokens - 1; i >= 0; i--) {
            TokenDesc& tk = tokens[i];

            lex_unput_token(tk.token, tk.text.c_str(), tk.lineno);
        }

        return false;
    }

    std::string function_name;
    int function_lineno;
    if (!operator_function_p) {
        if (tokens.size() > 0) {
            TokenDesc& tk = tokens[tokens.size() - 1];

            if (tk.token == tkIDENT) {
                function_name = tk.text;
                function_lineno = tk.lineno;
            }
        }
    }

    // ignore the parameters
    {
        std::string last_text;
        int last_lineno;
        int depth = 1;
        for (;;) {
            r = lex_get_token(text, &lineno);
            if (r == tkLEFTP) {
                ++depth;
            }
            else if (r == tkRIGHTP) {
                --depth;
                if (depth == 0)
                    break;
            }
            else if (r == tkIDENT) {
                last_text = text;
                last_lineno = lineno;
            }
            else if (r == 0)
                break;
            else if (r == -1)
                throw E_UNKNOW_TOKEN;
        }

        r = lex_get_token(text, &lineno);

        if (r == tkLEFTP) {
            ignore_pair(tkLEFTP, tkRIGHTP, true);

            if (last_text.size() > 0) {
                function_name = last_text;
                function_lineno = last_lineno;
            }
        }
        else {
            lex_unput_token(r, text.c_str(), lineno);
        }
    }

    // follow { ?
    r = lex_get_token(text, &lineno);

    if (r == tkCOLON) {
        // return-type function-name ( parameters ) :
        // class-member-initialize { ... }
        ignore_until_token(tkLEFTB);

        r = lex_get_token(text, &lineno);
    }

    if (r == rwCONST) {
        // const member function
        // type name ( parameters ) const ...
        r = lex_get_token(text, &lineno);
    }

    if (r == rwATTRIBUTE) {
        ignore_pair(tkLEFTP, tkRIGHTP, false);

        r = lex_get_token(text, &lineno);
    }

    // follow = 0; ?
    if (r == tkASS) {
        // ignore 0
        r = lex_get_token(text, &lineno);

        // it's ;
        r = lex_get_token(text, &lineno);
    }

    // put the result if it's the function declare ?
    // for example in the cpp file: static type name (arguments) ;
    // dont put here

    if (r == tkTERM || r == 0) {
        if ((g_header_file_p || g_inside_class_p)
            && function_name.size() > 0)
            RESULT_PUT_FUNCTION(function_name.c_str(), function_lineno);
        return true;
    }
    else if (r == -1)
        throw E_UNKNOW_TOKEN;

    else if (r != tkLEFTB) {
        lex_unput_token(r, text.c_str(), lineno);
        return true;
    }

    bool follow = true;
    if (function_name.size() > 0) {
        RESULT_PUT_FUNCTION(function_name.c_str(), function_lineno);
        tag_tree_follow();
    }
    else
        follow = false;

    // function content...
    proc_function_define();

    if (follow)
        tag_tree_up();

    return true;
}

// handle the function body
static void proc_function_define()
{
    // delete, not fetch the local variable.
    // only fetch the function references...
    std::string text, last_text;
    int         lineno, last_lineno;
    int         token, last_token;
    int         depth;

    depth = 1;

    while (depth > 0) {
        token = lex_get_token(text, &lineno);

        switch (token) {
        case tkLEFTP:
            if (last_token == tkIDENT)
                RESULT_PUT_REFERENCE(last_text.c_str(), last_lineno);
            break;

        case tkLEFTB:
            ++depth;
            break;

        case tkRIGHTB:
            --depth;
            break;

        case 0:
            depth = 0;
            break;

        case -1:
            throw E_UNKNOW_TOKEN;
            break;
        }

        last_text   = text;
        last_lineno = lineno;
        last_token  = token;
    }

#if 0
    std::string text;
    int         lineno;
    int         r;
    bool        done;
    int         depth;

    done  = false;
    depth = 1;
    while (!done) {
        r = lex_get_token(text, &lineno);

        switch (r) {
        case tkIDENT:
            lex_unput_token(r, text.c_str(), lineno);
            if (!try_variable_define()) {
                r = lex_get_token(text, &lineno);
                std::string text_bak;
                int lineno_bak;
                int r_bak = lex_get_token(text_bak, &lineno_bak);
                if (r_bak == tkCOLON)
                    break;
                else if (r_bak != tkTERM) {
                    lex_unput_token(r_bak, text_bak.c_str(), lineno_bak);
                    lex_unput_token(r, text.c_str(), lineno);
                    fetch_func_ref_until_term();
                }
            }
            break;

        case rwCLASS:
        case rwSTRUCT:
        case rwUNION:
            proc_struct_and_class_wrap(r);
            break;

        case rwENUM:
            proc_enum();
            break;

        case tkMACRO_DEFINE:
            proc_macro_define(text.c_str(), lineno);
            break;

        case rwIF:
        case rwWHILE:
        case rwFOR:
        case rwSWITCH:
        case rwCATCH:
            fetch_func_ref_from_pair();
            break;

        case rwUSING:
        case rwCONTINUE:
        case rwBREAK:
        case rwGOTO:
            ignore_until_token(tkTERM);
            break;

        case rwASM:
            ignore_pair(tkLEFTP, tkRIGHTP, false);

        case rwRETURN:
        case rwTHROW:
            fetch_func_ref_until_term();
            break;

        case rwCASE:
        case rwDEFAULT:
            ignore_until_token(tkCOLON);
            break;

        case rwTYPEDEF:
            proc_typedef();
            break;

        case tkLEFTB:
            ++depth;
            break;

        case tkRIGHTB:
            --depth;
            if (!depth)
                done = true;
            break;

        case 0:
            done = true;
            break;

        case -1:
            throw E_UNKNOW_TOKEN;
            break;

        default:
            fetch_func_ref_until_term();
        }
    }
#endif
}

// type [pointer|reference] name [\[] [ = initializ-value] [, ...] ;
// type = Parent::Child[<template>]
// name = tkIDENT
// and then {,|=|;}
static bool try_variable_define()
{
    TokenVector tokens;
    std::string text;
    int         lineno;
    int         r;

    // try the main type
    bool ex = true;

    for (;;) {
        r = lex_get_token(text, &lineno);

        if (r == 0)
            break;
        else if (r == -1)
            throw E_UNKNOW_TOKEN;

        if (ex) {
            if (r != tkIDENT) {
                lex_unput_token(r, text.c_str(), lineno);
                break;
            }
        }
        else {
            if (r != tkCC) {
                lex_unput_token(r, text.c_str(), lineno);
                break;
            }
        }

        tokens.push_back(TokenDesc(r, text.c_str(), lineno));

        ex = !ex;
    }

    if (tokens.empty())
        return false;

    // try <> template
    {
        r = lex_get_token(text, &lineno);

        if (r == tkLEFTA) {
            tokens.push_back(TokenDesc(r, text.c_str(), lineno));

            int depth = 1;

            bool template_predicate = false;
            while (depth > 0) {
                r = lex_get_token(text, &lineno);

                if (r > 0)
                    tokens.push_back(TokenDesc(r, text.c_str(), lineno));

                switch (r) {

                case tkRIGHTA:
                    --depth;
                    if (depth == 0) {
                        template_predicate = true;
                    }
                    break;

                case tkLEFTA:
                    ++depth;
                    break;

                case tkTERM:
                case 0:
                    depth = 0;
                    break;

                case -1:
                    throw E_UNKNOW_TOKEN;
                    break;
                }
            }

            if (template_predicate == false) {
                int i;
                for (i = tokens.size() - 1; i >= 0; i--) {
                    TokenDesc& tk = tokens[i];
                    lex_unput_token(tk.token, tk.text.c_str(), tk.lineno);
                }

                return false;
            }
        }
        else
            lex_unput_token(r, text.c_str(), lineno);
    }

    // and may be * and &
    for (;;) {
        r = lex_get_token(text, &lineno);

        if (r == 0)
            break;
        else if (r == -1)
            throw E_UNKNOW_TOKEN;

        if (r != tkPTR && r != tkREF) {
            lex_unput_token(r, text.c_str(), lineno);
            break;
        }
        else
            tokens.push_back(TokenDesc(r, text.c_str(), lineno));
    }

    int ptr_ref_end = tokens.size();

    // and the ident for variable name
    r = lex_get_token(text, &lineno);

    if (r == tkIDENT || r == rwCONST) {
        // copy until tkTERM
        tokens.push_back(TokenDesc(r, text.c_str(), lineno));

        bool ok = false;

        while (!ok) {
            r = lex_get_token(text, &lineno);

            if (r > 0)
                tokens.push_back(TokenDesc(r, text.c_str(), lineno));

            switch (r) {
            case tkTERM:
            case 0:
            case rwIF:
            case rwELSE:
            case rwDO:
            case rwWHILE:
            case rwFOR:
            case rwSWITCH:
                tokens.pop_back();
                if (r != tkTERM)
                    lex_unput_token(r, text.c_str(), lineno);
                ok = true;
                break;

            case -1:
                throw E_UNKNOW_TOKEN;
                break;

            case tkLEFTB:
                if (tokens.back().token != tkASS) {
                    int depth = 1;
                    while (depth > 0) {
                        r = lex_get_token(text, &lineno);
                        if (r > 0)
                            tokens.push_back(TokenDesc(r, text.c_str(), lineno));

                        switch (r) {
                        case tkLEFTB:
                            ++depth;
                            break;

                        case tkRIGHTB:
                            --depth;
                            break;

                        case tkTERM:
                        case 0:
                        case rwIF:
                        case rwELSE:
                        case rwDO:
                        case rwWHILE:
                        case rwFOR:
                        case rwSWITCH:
                            depth = 0;
                            tokens.pop_back();
                            if (r != tkTERM)
                                lex_unput_token(r, text.c_str(), lineno);
                            break;

                        case -1:
                            throw E_UNKNOW_TOKEN;
                            break;
                        }
                    }
                }
                break;
            }
        }

        fetch_variable_from_tokens(tokens, ptr_ref_end, false);

        return true;
    }
    // function pointer support
    // return-type (*name) (argument) ;
    if (r == tkLEFTP) {
        tokens.push_back(TokenDesc(r, text.c_str(), lineno));
        r = lex_get_token(text, &lineno);
        if (r == tkPTR) {
            // save the tkPTR *
            tokens.push_back(TokenDesc(r, text.c_str(), lineno));

            // () ()
            int         depth  = 1;
            std::string last_text;
            int         last_lineno;
            for (;;) {
                r = lex_get_token(text, &lineno);
                if (r > 0)
                    tokens.push_back(TokenDesc(r, text.c_str(), lineno));

                if (r == tkLEFTP)
                    ++depth;
                else if (r == tkRIGHTP) {
                    --depth;
                    if (depth == 0)
                        break;
                }
                else if (r == tkIDENT) {
                    last_text = text;
                    last_lineno = lineno;
                }
                else if (r == 0 || r == tkTERM)
                    break;
                else if (r == -1)
                    throw E_UNKNOW_TOKEN;
            }

            if (depth == 0) {
                r = lex_get_token(text, &lineno);

                if (r == tkLEFTP) {
                    if (last_text.size() > 0)
                        RESULT_PUT_VARIABLE(last_text.c_str(), last_lineno);

                    ignore_until_token(tkTERM);

                    return true;
                }
                else
                    lex_unput_token(r, text.c_str(), lineno);
            }
        }
        else
            lex_unput_token(r, text.c_str(), lineno);
    }
    else
        lex_unput_token(r, text.c_str(), lineno);

    {
        int i;
        for (i = tokens.size() - 1; i >= 0; i--) {
            TokenDesc& tk = tokens[i];
            lex_unput_token(tk.token, tk.text.c_str(), tk.lineno);
        }
    }

    return false;
}

int read_entire_file(const char  *fn,
                     char       **result)
{
#ifdef WIN32
    HANDLE h = CreateFile(fn, GENERIC_READ, 0, NULL, OPEN_EXISTING,
                          FILE_ATTRIBUTE_NORMAL, NULL);
    if (h == INVALID_HANDLE_VALUE) {
        fprintf(stderr, "open file `%s': %d\n",
                fn, GetLastError());
        return -1;
    }

    int size = GetFileSize(h, NULL);
    if (size <= 0) {
        CloseHandle(h);
        return -1;
    }

    char *buf = (char *) malloc (size + 1);

    if (buf == NULL) {
        fprintf(stderr, "alloc memory %d bytes failed: %s\n",
                size, strerror(errno));
        CloseHandle(h);
        return -1;
    }

    DWORD nbytes_read = 0;
    if (!ReadFile(h, buf, size, &nbytes_read, NULL)) {
        fprintf(stderr, "read file `%s' failed, %d\n",
                fn, GetLastError());
        CloseHandle(h);
        free(buf);
        return -1;
    }

    CloseHandle(h);

    if (size != nbytes_read) {
        free(buf);
        return -1;
    }

    buf[size] = 0;
    *result = buf;
    return size;

#else
    int fd = open(fn, O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "open file `%s' failed: %s\n",
                fn, strerror(errno));
        return -1;
    }
    struct stat st;
    if (fstat(fd, &st) < 0) {
        fprintf(stderr, "stat file `%s' failed: %s\n",
                fn, strerror(errno));
        close(fd);
        return -1;
    }

    int size = st.st_size;

    if (size == 0) {
        close(fd);
        return 0;
    }

    char *buf = (char *) malloc (size + 1);

    if (buf == NULL) {
        fprintf(stderr, "alloc memory %d bytes failed: %s\n",
                size, strerror(errno));
        close(fd);
        return -1;
    }

    int i = 0;
    while (i < size) {
        int r = read(fd, buf + i, size - i);
        if (r > 0)
            i += r;
        else if (r == 0)
            break;
        else if (r < 0 && errno != EINTR)
            break;
    }

    close(fd);

    if (i != size) {
        fprintf(stderr, "read `%s' failed: %s\n", fn, strerror(errno));
        free(buf);
        return -1;
    }

    buf[size] = 0;

    *result = buf;
    return size;
#endif
}

int main(int argc, char *argv[])
{
    int i = 1;
    while (i < argc) {
        char *buf;
        int   size = read_entire_file(argv[i], &buf);
        if (size > 0) {
            lex_reset_buffer(buf, size);
            init_tag_tree();

            g_filename = argv[i];

            g_header_file_p = is_header_file_name(argv[i]);

            try {
                top_level();

                if (g_tag_context != g_global_tag)
                    abort();

                // dump the filename
                printf("(change-file ");
                print_quote_string(argv[i]);
                printf(")\n");
                tag_tree_dump_quote(g_global_tag);
            }
            catch (Exception& e) {
                fprintf(stderr, "catch exception: `%s': %d, filename `%s', sl = %d\n",
                        e.desc.c_str(), e.line, argv[argc - 1], e.source_line);
            }

            free(buf);
        }
        ++i;
    }

    return 0;
}
